<script lang="ts" setup>
import { onMounted, reactive } from 'vue'
import { dbus } from '@/dbus/dbus.ts'
import { ComponentV2rayServer, new_component_v2ray_server } from '@/models/components/v2ray/v2ray_server.ts'
import { invoke_proxy } from '@/functions/invoke.ts'
import { BackendResponse } from '@/runtime/response.ts'
import { BACKEND_CODE_OK } from '@/runtime/code.ts'
import { read_from_clipboard } from '@/functions/clipboard.ts'
import ComponentV2rayEditServerConfig from '@/views/components/ComponentV2rayEditServerConfig.vue'
import { ComponentV2rayBasicSetting, new_component_v2ray_basic_setting } from '@/models/components/v2ray/v2ray_basic_setting.ts'
import { ComponentV2rayInfo } from '@/models/components/v2ray/v2ray_info.ts'
import { ComponentV2raySubscription, ComponentV2raySubscriptionLastUpdateStateNames } from '@/models/components/v2ray/v2ray_subscription.ts'
import ComponentV2rayEditSubscriptionConfig from '@/views/components/ComponentV2rayEditSubscriptionConfig.vue'
import dayjs from 'dayjs'

const state = reactive<{ show: boolean; basic_setting: ComponentV2rayBasicSetting; switching: boolean; servers: ComponentV2rayServer[]; importing: boolean; subscriptions: ComponentV2raySubscription[]; updating: boolean }>({ show: false, basic_setting: new_component_v2ray_basic_setting(), switching: false, servers: [], importing: false, subscriptions: [], updating: false })

onMounted(async () => {
    dbus.on('component:v2ray:show-server-management', show)
})

async function show(): Promise<void> {
    state.show = true

    const response = await invoke_proxy<BackendResponse<ComponentV2rayInfo>>('invoke_component_v2ray_info')
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    state.basic_setting = response.data.basic_setting

    await refresh_servers(false)
    await refresh_subscriptions(false)
}

async function switch_server(server: ComponentV2rayServer): Promise<void> {
    for (const server of state.servers) {
        server.used = false
    }

    server.used = true

    state.switching = true
    const response = await invoke_proxy<BackendResponse<any>>('invoke_component_v2ray_save_server', { server })
    state.switching = false
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    window.Message.success('切换成功')
    await refresh_servers(false)
}

async function remove_server(server: ComponentV2rayServer): Promise<void> {
    const response = await invoke_proxy<BackendResponse<any>>('invoke_component_v2ray_remove_server', { server })
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    window.Message.success('删除成功')
    await refresh_servers(false)
}

async function remove_subscription(subscription: ComponentV2raySubscription): Promise<void> {
    const response = await invoke_proxy<BackendResponse<any>>('invoke_component_v2ray_remove_subscription', { subscription })
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    window.Message.success('删除成功')
    await refresh_subscriptions(false)
}

async function import_from_clipboard(): Promise<void> {
    const content = (await read_from_clipboard()).trim()
    console.log(content)

    if ('' === content) {
        window.Message.error('未从剪贴板读取到内容')

        return
    }

    const server = parse_vmess_protocol(content)
    if ('string' === typeof server) {
        window.Message.error(server)

        return
    }

    dbus.emit('component:v2ray:edit-server-config', server)
}

async function import_from_clipboard_with_subscription(): Promise<void> {
    let content = (await read_from_clipboard()).trim()

    if ('' === content) {
        window.Message.error('未从剪贴板读取到内容')

        return
    }

    state.importing = true
    if (content.startsWith('http')) {
        const response = await invoke_proxy<BackendResponse<string>>('invoke_get_http_content', { url: content })
        if (BACKEND_CODE_OK !== response.code) {
            window.Message.error(response.message)

            return
        }

        content = response.data
    }

    const vmesses_string = atob(content)
    if (!vmesses_string) {
        window.Message.error('不是有效的BASE64字符串')

        return
    }

    const vmesses = vmesses_string.split('\n')
    if (0 == vmesses.length) {
        window.Message.error('未解析到有效的配置信息')

        return
    }

    for (const vmess of vmesses) {
        const server = parse_vmess_protocol(vmess)
        if ('string' === typeof server) {
            continue
        }

        await invoke_proxy<BackendResponse<any>>('invoke_component_v2ray_save_server', { server: server })
    }

    state.importing = false
    window.Message.success('导入完成')
    await refresh_servers(false)
}

function parse_vmess_protocol(content: string): ComponentV2rayServer | string {
    if (!content.startsWith('vmess://')) {
        return '剪贴板中的内容不是VMESS协议'
    }

    const vmess = content.substring(8)
    const config = JSON.parse(atob(vmess)) as { add: string; host: string; id: string; net: string; path: string; port: number; ps: string; tls: string; v: number; aid: number; type: string }
    if (!config.v || 2 != config.v) {
        return '不是受支持的VMESS协议版本'
    }

    const server = new_component_v2ray_server()
    server.tag = config.ps
    server.address = config.add
    server.port = parseInt(config.port.toString())
    server.user_id = config.id
    server.alter_id = parseInt(config.aid.toString())
    server.security = config.type
    server.network = { tcp: 'Tcp', kcp: 'Kcp', ws: 'WebSocket', http: 'Http', quic: 'Quic' }[config.net] as string
    if ('WebSocket' === server.network) {
        server.websocket_setting.path = config.path
        server.websocket_setting.headers_json_string = JSON.stringify({ Host: config.host })
    }

    return server
}

async function refresh_servers(message: boolean): Promise<void> {
    const response = await invoke_proxy<BackendResponse<ComponentV2rayServer[]>>('invoke_component_v2ray_servers')
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    state.servers = response.data
    if (message) {
        window.Message.success('刷新完成')
    }
}

async function refresh_subscriptions(message: boolean): Promise<void> {
    const response = await invoke_proxy<BackendResponse<ComponentV2raySubscription[]>>('invoke_component_v2ray_subscriptions')
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    state.subscriptions = response.data
    if (message) {
        window.Message.success('刷新完成')
    }
}

async function update_subscription(subscription: ComponentV2raySubscription): Promise<void> {
    state.updating = true
    const response = await invoke_proxy<BackendResponse<any>>('invoke_component_v2ray_update_subscription', { subscription })
    state.updating = false
    if (BACKEND_CODE_OK !== response.code) {
        window.Message.error(response.message)

        return
    }

    await refresh_subscriptions(false)
    await refresh_servers(false)
    window.Message.success('刷新完成')
}
</script>

<template>
    <n-modal v-model:show="state.show" :mask-closable="false" class="modal" transform-origin="center">
        <n-card closable @close="state.show = false">
            <n-space :wrap="false" :wrap-item="false" vertical>
                <n-space :wrap="false" :wrap-item="false" align="center" justify="center">
                    <div style="width: 120px">
                        <n-button class="max-width" size="small" type="info" @click="refresh_subscriptions(true)">刷新订阅列表</n-button>
                    </div>
                    <div class="max-flex-width">
                        <n-button class="max-width" size="small" type="info" @click="dbus.emit('component:v2ray:edit-subscription-config', new_component_v2ray_server())">添加服务器订阅</n-button>
                    </div>
                </n-space>
                <div class="subscripts">
                    <n-space :wrap="false" :wrap-item="false" vertical>
                        <template v-for="subscription in state.subscriptions" :key="subscription.uuid">
                            <n-space :wrap="false" :wrap-item="false" align="center" style="line-height: 1.5">
                                <div class="text-ellipsis max-flex-width">订阅名称: {{ subscription.name }}</div>
                                <div style="width: 80px; min-width: 80px">状态: {{ subscription.enabled ? '已启用' : '未启用' }}</div>
                                <div style="width: 100px; min-width: 100px">更新: {{ subscription.auto_update ? '自动更新' : '手动更新' }}</div>
                                <div style="width: 220px; min-width: 220px">最后更新时间: {{ subscription.last_update_time ? dayjs.unix(subscription.last_update_time).format('YYYY-MM-DD HH:mm:ss') : '未运行' }}</div>
                                <div style="width: 140px; min-width: 140px">最后更新状态: {{ ComponentV2raySubscriptionLastUpdateStateNames[subscription.last_update_state] }}</div>
                                <n-space :wrap="false" :wrap-item="false" align="center">
                                    <n-button :disabled="state.switching || state.updating" size="small" type="info" @click="update_subscription(subscription)">更新</n-button>
                                    <n-button :disabled="state.switching || state.updating" size="small" type="info" @click="dbus.emit('component:v2ray:edit-subscription-config', subscription)">编辑</n-button>
                                    <n-button :disabled="state.switching || state.updating" size="small" type="error" @click="remove_subscription(subscription)">删除</n-button>
                                </n-space>
                            </n-space>
                        </template>
                    </n-space>
                </div>
                <n-space :wrap="false" :wrap-item="false" align="center" justify="center">
                    <div style="width: 120px">
                        <n-button class="max-width" size="small" type="info" @click="refresh_servers(true)">刷新服务器列表</n-button>
                    </div>
                    <div style="width: 360px">
                        <n-button :disabled="state.importing" :loading="state.importing" class="max-width" size="small" type="info" @click="import_from_clipboard_with_subscription">从剪贴板导入服务器订阅(单次)</n-button>
                    </div>
                    <div style="width: 240px">
                        <n-button class="max-width" size="small" type="info" @click="import_from_clipboard">从剪贴板导入分享链接</n-button>
                    </div>
                    <div class="max-flex-width">
                        <n-button class="max-width" size="small" type="info" @click="dbus.emit('component:v2ray:edit-server-config', new_component_v2ray_server())">手动添加服务器配置</n-button>
                    </div>
                </n-space>
                <n-space :wrap="false" :wrap-item="false" class="servers" vertical>
                    <template v-for="server in state.servers" :key="server.uuid">
                        <n-space :wrap="false" :wrap-item="false" align="center" style="line-height: 1.5">
                            <div style="width: 60px; min-width: 60px">{{ state.basic_setting.enable_balancer ? '负载均衡' : server.used ? '使用中' : '' }}</div>
                            <div class="text-ellipsis" style="width: 300px">标签: {{ server.tag }}</div>
                            <div class="text-ellipsis" style="width: 300px">服务器: {{ server.address + ':' + server.port.toString() }}</div>
                            <div class="text-ellipsis" style="width: 200px">用户ID: {{ server.user_id }}</div>
                            <div class="max-flex-width" style="min-width: 200px">网络连接方式: {{ server.network }}</div>
                            <n-space :wrap="false" :wrap-item="false" align="center">
                                <n-button :disabled="state.switching || state.updating" size="small" type="info" @click="switch_server(server)">切换</n-button>
                                <n-button :disabled="state.switching || state.updating" size="small" type="info" @click="dbus.emit('component:v2ray:edit-server-config', server)">编辑</n-button>
                                <n-button :disabled="state.switching || state.updating" size="small" type="error" @click="remove_server(server)">删除</n-button>
                            </n-space>
                        </n-space>
                    </template>
                </n-space>
            </n-space>
            <ComponentV2rayEditServerConfig @saved="refresh_servers(false)"></ComponentV2rayEditServerConfig>
            <ComponentV2rayEditSubscriptionConfig @saved="refresh_subscriptions(false)"></ComponentV2rayEditSubscriptionConfig>
        </n-card>
    </n-modal>
</template>

<style lang="scss" scoped>
.modal {
    width: 80vw;
    height: 80vh;
    min-height: 600px;
    overflow: hidden;
}

.subscripts {
    min-height: 200px;
    max-height: 200px;
    overflow-y: auto;
}

.servers {
    min-height: 200px;
    max-height: 40vh;
    overflow-y: auto;
}
</style>
